#include "BlockingQueue.h"
#include <chrono>

namespace Ark
{
    //---------- Task ----------
    BlockingQueue::Task::Task()
    {
    }

    BlockingQueue::Task::Task(const std::function<void()>& task) :task(task)
    {
    }

    BlockingQueue::Task::Task(const std::function<void()>&& task) :task(task)
    {
    }

    //---------- BlockingQueue ----------
    BlockingQueue::BlockingQueue()
    {
    }

    BlockingQueue::~BlockingQueue()
    {
    }

    void BlockingQueue::Push(TaskPtr task)
    {
        if (!task) {
            return;
        }

        std::lock_guard<std::mutex> lock(_mutex);
        if (_waitCount > 0) {
            _cond.notify_one();
        }
        _taskList.push_back(task);
    }

    Ark::BlockingQueue::TaskPtr BlockingQueue::Pop(int waitSeconds)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        while (_taskList.empty()) {

            ++_waitCount;
            auto status = std::cv_status::no_timeout;
            if (waitSeconds == WAIT_INFINITE) {
                _cond.wait(lock);
            }
            else {
                status = _cond.wait_for(lock, std::chrono::duration<int>(waitSeconds));
            }
            --_waitCount;
        
            if (status == std::cv_status::timeout) {
                return nullptr;
            }
        }

        // while wait success, we already have the lock
        TaskPtr task = _taskList.front();
        _taskList.pop_front();
        return task;
    }

    bool BlockingQueue::IsEmpty()
    {
        std::lock_guard<std::mutex> lock(_mutex);
        return _taskList.empty();
    }

}